/*
 * usb_l.h
 *
 *  Created on: Jul 5, 2013
 *      Author: g.kruglov
 */

#ifndef USB_L_H_
#define USB_L_H_

#include "ch.h"
#include "hal.h"
#include "stm32_usb.h"
#include "kl_lib_L15x.h"
#include "descriptors.h"
#include "buf_sizes.h"

enum EpState_t {esIdle, esSetup, esDataIn, esDataOut, esStatusIn, esStatusOut, esError};

class Ep_t {
private:
    uint8_t Indx;
public:
    EpState_t State;
    Buf_t BufIn;
    bool TransmitFinalZeroPkt;
    InputQueue *POutQueue;
    void Init(const EpCfg_t *PCfg);
    void StallIn()  { EPR_SET_STAT_TX(Indx, EPR_STAT_TX_STALL); }
    void StallOut() { EPR_SET_STAT_RX(Indx, EPR_STAT_RX_STALL); }
    void FillInBuf();
    inline void StartInTransaction()  { EPR_SET_STAT_TX(Indx, EPR_STAT_TX_VALID); }
    inline void StartOutTransaction() { EPR_SET_STAT_RX(Indx, EPR_STAT_RX_VALID); }
    void TransmitDataChunk() { FillInBuf(); StartInTransaction(); }
    void TransmitZeroPkt();
    void ReceiveZeroPkt();
    void ReadToBuf(uint8_t *PDstBuf, uint16_t Len);
    void ReadToQueue(uint16_t Len);
    uint16_t GetRxDataLength();
    void FlushRx(uint16_t Len);
    ftVoidVoid cbEndTransaction;
    Thread *PThread;
    inline void ResumeWaitingThd(msg_t ReadyMsg);
};

struct UsbSetupReq_t {
    uint8_t  bmRequestType;
    uint8_t  bRequest;
    union {
        uint16_t wValue;
        struct {
            uint8_t Indx;
            uint8_t Type;
        };
    };
    uint16_t wIndex;
    uint16_t wLength;
} __attribute__ ((__packed__));

enum UsbState_t {usDisconnected, usConnected, usConfigured};

class Usb_t {
private:
    Ep_t Ep[EP_CNT];
    uint8_t Ep0InBuf[EP0_SZ];
    union {
        uint8_t Ep0OutBuf[EP0_SZ];
        UsbSetupReq_t SetupReq;
    };
    uint8_t Ep1OutBuf[EP0_SZ];
    void IReset();
    void ICtrHandlerIN(uint16_t EpID);
    void ICtrHandlerOUT(uint16_t EpID, uint16_t Epr);
    uint16_t PktMemNext;
    void PktMemReset();
    uint16_t PktMemAlloc(uint16_t Sz);
    void SetupPktHandler();
    EpState_t DefaultReqHandler(Buf_t *PBuf);
    void PrepareInTransaction(Buf_t Buf);
public:
    UsbState_t State;
    void Init();
    void Connect()    { SYSCFG->PMC |=  SYSCFG_PMC_USB_PU; State = usConnected; }
    void Disconnect() { SYSCFG->PMC &= ~SYSCFG_PMC_USB_PU; State = usDisconnected; }
    // Data operations
    void StartTransmitBuf(uint8_t EpID, Buf_t ABuf);
    uint8_t WaitTransactionEnd(uint8_t EpID);
    inline void StartReception(uint8_t EpID) { Ep[EpID].StartOutTransaction(); }
    inline void AssignEpOutQueue(uint8_t EpID, InputQueue *PQueue) { Ep[EpID].POutQueue = PQueue; }
    // Inner use
    void IIrqHandler();
    uint16_t IGetSetupAddr() { return SetupReq.wValue; }
    friend class Ep_t;
};

extern Usb_t Usb;

// ============================== Constants ====================================
#define USB_REQ_GET_STATUS                  0
#define USB_REQ_CLEAR_FEATURE               1
#define USB_REQ_SET_FEATURE                 3
#define USB_REQ_SET_ADDRESS                 5
#define USB_REQ_GET_DESCRIPTOR              6
#define USB_REQ_SET_DESCRIPTOR              7
#define USB_REQ_GET_CONFIGURATION           8
#define USB_REQ_SET_CONFIGURATION           9
#define USB_REQ_GET_INTERFACE               10
#define USB_REQ_SET_INTERFACE               11
#define USB_REQ_SYNCH_FRAME                 12

#define USB_FEATURE_ENDPOINT_HALT           0
#define USB_FEATURE_DEVICE_REMOTE_WAKEUP    1
#define USB_FEATURE_TEST_MODE               2

#define USB_RTYPE_DIR_DEV2HOST              0x80
#define USB_RTYPE_RECIPIENT_MASK            0x1F
#define USB_RTYPE_RECIPIENT_DEVICE          0x00
#define USB_RTYPE_RECIPIENT_INTERFACE       0x01
#define USB_RTYPE_RECIPIENT_ENDPOINT        0x02
#define USB_RTYPE_RECIPIENT_OTHER           0x03


#endif /* USB_L_H_ */
